using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace Woz
{
    /// <summary>
    /// lock action that locks entities
    /// </summary>
    internal class LockAction : Woz.Action
    {
        /// <summary>
        /// default constructor
        /// </summary>
        /// <param name="pEntities">the entities that need to be locked</param>
        public LockAction(params Woz.Entity[] pEntities)
        {
            // save the entity
            this.mEntities = pEntities;

            // create the cloned entities
            this.mClonedEntities = new Woz.Entity[pEntities.Length];

            // create the locks
            this.mLocks = new bool[pEntities.Length];

            // initialize the locks
            for (int i = 0; i < pEntities.Length; i++)
            {
                this.mLocks[i] = pEntities[i].Locked;
            }

            // make action string
            if (this.mEntities.Length == 1)
            {
                this.mActionString = String.Concat("Locked ", this.mEntities[0].ToString());
            }
            else
            {
                this.mActionString = String.Format("Locked {0} Entities", this.mEntities.Length);
            }
        }

        /// <summary>
        /// gets the name of the tool this action prefers
        /// </summary>
        public override string ToolName
        {
            get
            {
                return "Select";
            }
        }

        /// <summary>
        /// gets whether this action can be propagated or not
        /// </summary>
        public override bool Propagatable
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// locks all of the unlocked entities
        /// </summary>
        /// <param name="pScreen">screen to lock entities on</param>
        protected override void OnExecute(Woz.Screen pScreen)
        {
            int zDepth;
            Woz.Entity entity;
            for (int i = 0; i < this.mEntities.Length; i++)
            {
                entity = this.mEntities[i];

                // if this entity is also referenced in another screen
                if (entity.RefCount > 1)
                {
                    // get a clone of the entity
                    this.mClonedEntities[i] = entity.Clone() as Woz.Entity;

                    // get the zdepth of the entity
                    zDepth = pScreen.GetZDepth(entity);

                    // remove the entity from the screen
                    pScreen.DeselectEntity(entity, false);
                    pScreen.RemoveEntity(entity, false);

                    // set entity to the cloned entity
                    entity = this.mClonedEntities[i];

                    // insert the clone into the same spot as the entity
                    pScreen.InsertEntity(entity, zDepth, false);
                    pScreen.SelectEntity(entity, false);
                }
                else
                {
                    // set the cloned entity to null
                    this.mClonedEntities[i] = null;
                }

                // lock it
                entity.Locked = true;
            }

            // update the entire screen
            Woz.ScreenManager.Instance.RequestSelectedScreenUpdate(pScreen, 
                Woz.InkSpaceConverter.Instance.InkSpaceCanvasRectangle);
        }

        /// <summary>
        /// unlocks all of the locked entities
        /// </summary>
        /// <param name="pScreen">screen to unlock entities on</param>
        protected override void OnUnExecute(Woz.Screen pScreen)
        {
            int zDepth;
            for (int i = 0; i < this.mClonedEntities.Length; i++)
            {
                if (this.mClonedEntities[i] != null)
                {
                    // get the zdepth
                    zDepth = pScreen.GetZDepth(this.mClonedEntities[i]);

                    // remove cloned entity from screen
                    pScreen.DeselectEntity(this.mClonedEntities[i], false);
                    pScreen.RemoveEntity(this.mClonedEntities[i], false);

                    // insert the original entity to screen where it used to be
                    pScreen.InsertEntity(this.mEntities[i], zDepth, false);
                    pScreen.SelectEntity(this.mEntities[i], false);
                }
                else
                {
                    // reset the locking
                    this.mEntities[i].Locked = this.mLocks[i];
                }
            }

            // update the entire screen
            Woz.ScreenManager.Instance.RequestSelectedScreenUpdate(pScreen, 
                Woz.InkSpaceConverter.Instance.InkSpaceCanvasRectangle);
        }

        /// <summary>
        /// check to see if this action can be applied to the given screen
        /// </summary>
        /// <param name="pScreen">the screen to check</param>
        /// <returns>true if the action can be applied to the given screen</returns>
        public override bool CanApplyTo(Screen pScreen)
        {
            // make sure all entities are contained in this screen
            foreach (Woz.Entity entity in this.mEntities)
            {
                if (!pScreen.Contains(entity))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// the list of entities that will be locked
        /// </summary>
        private Woz.Entity[] mEntities;

        /// <summary>
        /// the list of cloned entities that will be locked
        /// </summary>
        private Woz.Entity[] mClonedEntities;

        /// <summary>
        /// the list of locks corresponding to the entities
        /// </summary>
        private bool[] mLocks;
    }
}
